package com.syf.kit.core.dao.impl;

import com.syf.kit.core.dao.mapper.MapperType;
import com.syf.kit.core.dao.mapper.registry.SyfAbstractDtoMapperRegistry;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.shardingsphere.api.hint.HintManager;

/**
 * 读写分离的情况下，默认select使用读库，update动作使用写库（自动提交）.
 * 利用ShardingJDBC实现，传入的是与ShardingJDBC的DataSource对应的sqlSessionFactory，由此屏蔽dao层的改动，减少复杂度
 * <p>
 * 当开启事务时，强制使用写库的连接（不自动提交），且无论select还是update。此处需要使用ShardingJDBC的强制路由。
 * 由于ShardingJDBC的强制路由是基于ThreadLocal实现，因此需要使用dao manager事务之间的代码，必须运行在同一线程内，或者更换线程时，新建的线程应该复制ThreadLocal
 */
public class SyfRWRdsDaoUtil extends SyfSimpleRdsDaoUtil {

    /**
     * ShardingJDBC中强制路由的管理器
     */
    private HintManager hintManager;

    /**
     * 此处的SqlSessionFactory必须绑定ShardingJDBC的DataSource
     */
    public SyfRWRdsDaoUtil(SqlSessionFactory sqlSessionFactory, SyfAbstractDtoMapperRegistry mapperRegistry, boolean autoCommit, MapperType mapperType) {
        super(sqlSessionFactory, mapperRegistry, autoCommit, mapperType);
    }

    @Override
    public void close() {
        // 关闭前，去掉该ThreadLocal
        if (this.hintManager != null) {
            hintManager.close();
            hintManager = null;
        }

        super.close();
    }

    @Override
    public void beginTransaction() {
        // ShardingJDBC中强制路由的管理器，强制当前线程使用写库
        try (HintManager tempHitManager = HintManager.getInstance()) {
            this.hintManager = tempHitManager;
            hintManager.setMasterRouteOnly();
        }

        super.beginTransaction();
    }

    @Override
    public void closeTransaction() {
        // 取消写库的强制路由
        if (this.hintManager != null) {
            hintManager.close();
        }

        super.closeTransaction();
    }

    @Override
    public void commitTransaction() {
        super.commitTransaction();
    }

    @Override
    public void rollbackTransaction() {
        super.rollbackTransaction();
    }
}
